use pest::Parser;
use pest_derive::Parser;
// use std::any::type_name;

// fn type_of<T>(_: &T) -> &'static str {
//     type_name::<T>()
// }

#[derive(Parser)]
#[grammar = "../sysy.pest"]
pub struct ExpressionParser;

#[derive(Debug)]
pub struct TokenWithLine {
    pub value: String,
    pub token: Rule,
    pub line: usize,
}

pub fn tokenize(content: &str) -> Vec<TokenWithLine> {
    match ExpressionParser::parse(Rule::program, content) {
        Ok(pairs) => pairs
            .flat_map(|pair| pair.into_inner())
            .filter(|token| token.as_rule() != Rule::EOI)
            .map(|token| {
                let (line, _) = token.line_col();
                let mut target = TokenWithLine {
                    token: token.as_rule(),
                    line: line,
                    value: token.as_str().to_string(),
                };
                if token.as_rule() == Rule::INTEGER_CONST {
                    for sub_rule in token.into_inner() {
                        let value = sub_rule.as_str().to_string();
                        let num = match sub_rule.as_rule() {
                            Rule::HEX_INTEGER => i64::from_str_radix(&value[2..], 16).unwrap(),
                            Rule::OCT_INTEGER => i64::from_str_radix(&value[1..], 8).unwrap(),
                            _ => i64::from_str_radix(&value[0..], 10).unwrap(),
                        };
                        target.value = num.to_string();
                    }
                }
                target
            })
            .collect(),
        Err(e) => {
            let te = e.clone();
            let line_content = te.line();
            let location = e.line_col;

            match location {
                pest::error::LineColLocation::Pos((line, col)) => {
                    eprintln!(
                        "Error type A at Line {line}: Mysterious character \"{}\".",
                        line_content.chars().nth(col - 1).unwrap(),
                    );
                }
                pest::error::LineColLocation::Span((sline, scol), (eline, ecol)) => {
                    eprintln!(
                        "Error type A at Line {sline}: Mysterious character \"{}\".",
                        line_content.chars().nth(scol - 1).unwrap(),
                    );
                    eprintln!(
                        "Error type A at Line {eline}: Mysterious character \"{}\".",
                        line_content.chars().nth(ecol - 1).unwrap(),
                    );
                }
            }
            vec![]
        }
    }
}
